/**
 * \file Thread.h
 * \brief Thread facilities
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <ktypes.h>
#include <platform/fpu.h>
#include <Reference.h>
#include <Sync.h>
#include <Queue.h>

#ifndef __KERNEL_THREAD_H__
#define __KERNEL_THREAD_H__

class Thread;
DEF_REF_TYPE(Thread);

#include <Process.h>

typedef void (*ThreadEntryPoint)(void *);

#define THREAD_PRIORITY_IDLE 0
#define THREAD_PRIORITY_MIN 1
#define THREAD_PRIORITY_MAX 63
#define THREAD_PRIORITY_NORMAL 32

#define THREAD_KSTACK_SIZE (8*1024)
#define THREAD_USTACK_SIZE (6*1024*1024)
#define THREAD_USTACK_INCR (64*1024)

typedef enum {
	THREAD_TYPE_KERNEL=0,
	THREAD_TYPE_USER,
	THREAD_TYPE_VM86,
} thread_type_t;

class Thread : public QueueNode {
	protected:
	
	char name[256];
	tid_t tid;
	
	const thread_type_t type;
	
	RefProcess process;
	
	// self reference
	RefThread self;
	
	// kernel stack base and top
	uint32 *kstack;
	uint32 *kstackTop;
	
	// saved stack pointer on context switch
	uint32 savedStackPointer;
	
	// FPU context and state info
	fpu_context_t *fpuContext;
	uint32 fpuState;
	
	// individual thread errno
	int errno;
	
	// thread local storage address
	void *tls;
	
	// thread priority
	int priority;
	int floatingPriority;
	
	// current queue, -1 when not queued
	int queue;
	
	// time slice quanta
	short slices;
	
	// time left to sleep in microseconds
	uint32 sleepTime;
	
	// statistics
	uint64 totalTicks;
	
	InterruptLock lock;
	
	Thread *next;
	Thread *prev;
	
	Thread(thread_type_t type);
	
	Thread(ThreadEntryPoint entry, void *arg, const char *name,
		RefProcess process=0);
	
	void save(regs_t *regs);
	void restore(regs_t *regs);
	
	// fault handler for thread related faults
	// subclasses can override this method to spport their required handling ops
	virtual bool fault(regs_t *regs);
	
	static void reschedule(regs_t *regs);
	static void switchThreads(void);
	
	static bool tickHandler(regs_t *regs);
	static bool syscallHandler(regs_t *regs);
	static bool faultHandler(regs_t *regs);
	
	static void queueRemove(Thread *node, int queue);
	static void queueAppend(Thread *node, int queue);
	static void queuePrepend(Thread *node, int queue);
	
	static void idle(void *);
	
	public:
	
	static void initialize(void);
	static void begin(void);
	
	static RefThread createThread(ThreadEntryPoint entry, void *arg,
		const char *name, RefProcess process);
		
	static RefThread getCurrentThread(void);
	static RefThread getThread(tid_t tid);
	
	virtual ~Thread(void);
	
	tid_t getTid(void);
	const char *getName(void);
	RefProcess getProcess(void);
	
	int *getErrno(void);
	
	void run(void);
	void kill(void);
	void wait(void);
	void notify(void);
	void sleep(uint32 utime);
	void yield(void);
	
	int getPriority(void);
	void setPriority(int p);
	
	int getFloatingPriority(void);
	void setFloatingPriority(int fp);
	
};

typedef struct {
	int ordinal;
	void *data;
	void (*destructor) (void *);
} UserTls;

DEF_REF_TYPE(UserTls);

class UserThread : public Thread {
	protected:
	
	// user mode stack
	uint32 *ustack;
	
	// virtual address of the user stack
	// initial top starts at ustackEa + THREAD_USTACK_SIZE
	uint32 ustackEa;
	
	// stack segment mapping to keep track of each additional segment added to
	// the stack as it grows. holds physical addresses of segments.
	uint32 segmentMapping[THREAD_USTACK_SIZE/THREAD_USTACK_INCR];
	uint32 nextSegment;
	
	// thread local storage
	HandleMap<RefUserTls> local;
	int ordinal;
	
	virtual bool fault(regs_t *regs);
	
	UserThread(ThreadEntryPoint entry, void *arg, const char *name,
		RefProcess process=0);
	
	void growStack(void);
	void shrinkStack(void);
	bool adjustStack(regs_t *regs);
	
	public:
	
	static RefThread createThread(ThreadEntryPoint entry, void *arg,
		const char *name, RefProcess process);
	
	void *getLocal(uint32 key);
	void setLocal(uint32 key, void *data);
	uint32 createKey(void *data, void (*destructor)(void *));
	void destroyKey(uint32 key);
	
	virtual ~UserThread(void);
};

class VM86Thread : public Thread {
	protected:
	
	bool vif;
	
	virtual bool fault(regs_t *regs);
	
	bool emulateInstruction(regs_vm86_t *regs);
	
	VM86Thread(ThreadEntryPoint entry, void *arg, const char *name,
		RefProcess process=0);
	
	public:
	
	static RefThread createThread(ThreadEntryPoint entry, void *arg,
		const char *name, RefProcess process);
	
	virtual ~VM86Thread(void);
};
	

#endif
